#include "TextManipulator.h"

short int InitializeStructFile(const char *path_fname) {

    filebuffer = malloc(sizeof(FileStruct));
    filebuffer->fd = open(path_fname, O_RDONLY);
    if(filebuffer->fd == -1) {
        printf("Cannot open the file!\n");
        return 1;
    }
    ReadFile();
    AlocateDataFile();
    filebuffer->rest = filebuffer->amountphrases % NTHREADS;    //se a divisao nao for exata, armazena o resto e distribui entre as threads
    filebuffer->phrases_thread = filebuffer->amountphrases/NTHREADS;
    close(filebuffer->fd);

    return 0;
}

void ReadFile() {
    struct stat buf;
    char buffer_aux[BLOCKSIZE+1];
    ssize_t nbytes;

    fstat(filebuffer->fd, &buf);
    filebuffer->size = (unsigned long int)buf.st_size;
    filebuffer->memory = malloc(filebuffer->size*sizeof(char));
    while((nbytes = read(filebuffer->fd, buffer_aux, BLOCKSIZE)) > 0) {     //le todo o conteudo do arquivo
        strncat(filebuffer->memory, buffer_aux, nbytes);
    }
}

void AlocateDataFile() {
    int c;
    unsigned register int i = 0;
    short int flag;

    filebuffer->amountphrases = 0;
    filebuffer->count_phrase = 0;
    filebuffer->count_word = 0;
    while(i < filebuffer->size) {
        c = filebuffer->memory[i];
        if(c == ' ') {  //se for espaco, entao prosegue ateh nao encontrar mais espaco
            while((c = filebuffer->memory[++i]) == ' ');
        }
        if(c == '\t') {
            filebuffer->positionph[filebuffer->amountphrases++] = i;    //se for \t eh uma frase
            i++;
        }
        else if(c == '[') {     //se o texto estiver entre colchetes, observa se ha frases dentro dele
            flag = 0;
            filebuffer->positionph[filebuffer->amountphrases++] = i;
            while((c = filebuffer->memory[++i]) != ']') {
                if(c == '.' || c == '!' || c == '?' || c == ';') {
                    if(flag == 0)
                        flag = 1;
                    filebuffer->positionph[filebuffer->amountphrases++] = i;
                }
            }
            if(flag == 0) {     //se flag = 0, entao nao encontrou nenhuma frase. Finaliza como frase agora
                filebuffer->positionph[filebuffer->amountphrases++] = i;
                i++;
            }
        }
        else if(c == '.') {     //se for ponto e o caracter posterior for letra ou numero, nao armazena como frase
                if(((filebuffer->memory[i+1] >= 48) && (filebuffer->memory[i+1] <= 57)) || ((filebuffer->memory[i+1] >= 97) && (filebuffer->memory[i+1] <= 122)) || (filebuffer->memory[i+1] >= 65 && filebuffer->memory[i+1] <= 90) || filebuffer->memory[i+1] == ')') {
                    i++;
                }
                else { //senao, eh uma frase
                    filebuffer->positionph[filebuffer->amountphrases++] = i;
                    i++;
                }
            }
        else if(c == '?' || c == ';' || c == '!') {     //se for um desses caracteres, eh uma frase
            filebuffer->positionph[filebuffer->amountphrases++] = i;
            i++;
        }
        else if((c == '\r') && (filebuffer->memory[i+1] == '\r')) {     //se for um desses caracteres, eh uma frase
            filebuffer->positionph[filebuffer->amountphrases++] = i;
            i += 2;
        }
        else if((c == '\r') && (filebuffer->memory[i+1] == '\t')) {     //se for um desses caracteres, eh uma frase
            i += 2;
        }
        else if((c == '-') && (filebuffer->memory[i+1] == '-')) {       //considera o conjunto de - como uma frase (sera ignorado depois)
            filebuffer->positionph[filebuffer->amountphrases++] = i;
            while(filebuffer->memory[++i] == '-');
            filebuffer->positionph[filebuffer->amountphrases++] = i-1;
        }
        else {
            i++;
        }
    }
    filebuffer->positionph[filebuffer->amountphrases++] = i;    //o final do arquivo tambem eh final de frase
}

unsigned long int ReturnWords(char **word) {
    char c, aux;
    unsigned register int i, j;
    unsigned long int count = 0, pos = 0, nphrases;

    nphrases = ReturnPhrasesPerThread();    //obtem a quantidade de frases para a thread que chamou ReturnWords()
    if(filebuffer->count_word == 0) {
        for(j = 0; j < filebuffer->positionph[0]; j++) {    //daqui em diante, percorre todas as frases dentro do limite nphrases e verifica se eh letra ou numero. Se for, entao copia para o buffer da matriz de frases
            c = filebuffer->memory[j];
            if((c >= 65 && c <= 90) || (c >= 97 && c <= 122) || (c >= 48 && c <= 57)) { //se for letra ou numero copia para a matriz
                c = toupper(c);
                word[count][pos++] = c;
            }
            else {
                aux = filebuffer->memory[j-1];  //se nao for letra ou num, e o caracter anterior for letra ou numero, entao marca como sendo uma palavra
                if((aux >= 65 && aux <= 90) || (aux >= 97 && aux <= 122) || (aux >= 48 && aux <= 57)) {
                    word[count++][pos] = '\0';
                    pos = 0;
                }
            }
        }
        filebuffer->count_word++;
        word[count++][pos] = '\0';
        pos = 0;
    }
    for(i = filebuffer->count_word; i < (filebuffer->count_word+nphrases)-1; i++) {
        for (j = filebuffer->positionph[i-1]+1; j <= filebuffer->positionph[i]; j++) {
            c = filebuffer->memory[j];
            if((c >= 65 && c <= 90) || (c >= 97 && c <= 122) || (c >= 48 && c <= 57)) { //se for letra ou numero copia para a matriz
                c = toupper(c);
                word[count][pos++] = c;
            }
            else {
                aux = filebuffer->memory[j-1];  //idem ao else anterior
                if((aux >= 65 && aux <= 90) || (aux >= 97 && aux <= 122) || (aux >= 48 && aux <= 57)) {
                    word[count++][pos] = '\0';
                    pos = 0;
                }
            }
        }
    }
    filebuffer->count_word += nphrases;   //contador incrementa, para que a proxima thread possa receber as palavras seguintes

    return count;
}

unsigned long int ReturnPhrases(char **phrase, unsigned long int nphrases) {
    char c;
    unsigned register int i, j;
    unsigned long int count = 0, pos = 0;

    if(filebuffer->count_phrase == 0) {
        for(j = 0; j < filebuffer->positionph[0]; j++) {    //daqui em diante, percorre as frases dentro do limite nphrases. Tudo que for letra eh copiado para a matriz
            c = filebuffer->memory[j];
            if((c >= 65 && c <= 90) || (c >= 97 && c <= 122) || (c >= 48 && c <= 57))  {
                c = toupper(c);
                phrase[count][pos++] = c;
            }
        }
        phrase[count++][pos] = '\0';    //se chegou aqui eh pq encontrou fim de frase. Entao, marca como frase
        filebuffer->count_phrase++;
        pos = 0;
    }
    for(i = filebuffer->count_phrase; i < (filebuffer->count_phrase+nphrases)-1; i++) {     //idem
        for (j = filebuffer->positionph[i-1]+1; j < filebuffer->positionph[i]; j++) {
            c = filebuffer->memory[j];
            if((c >= 65 && c <= 90) || (c >= 97 && c <= 122) || (c >= 48 && c <= 57))  {
                c = toupper(c);
                phrase[count][pos++] = c;
            }
        }
        if(pos != 0) {
            phrase[count++][pos] = '\0';
            pos = 0;
        }
    }
    filebuffer->count_phrase += nphrases;  //contador incrementa, para que a proxima thread possa receber as frases seguintes

    return count;
}

unsigned long int ReturnPhrasesPerThread() {
    if(filebuffer->rest != 0){      //se houver frases sobrando, entao distribui para as threads
        filebuffer->rest--;
        return filebuffer->phrases_thread+1;
    }
    else    //senao, retorna a qnt exata de frases por thread calculado
        return filebuffer->phrases_thread;
}

void ResetFlags() {     //reinicializa variavel que armazena a quantidade de frases que sobram
    filebuffer->rest = filebuffer->amountphrases % NTHREADS;
}

void FreeMemory() {     //libera espaco
    free(filebuffer->memory);
    free(filebuffer);
}
